#include "BlockQueue.hpp" //自己封装的阻塞队列
#include <vector>
#include "Thread.hpp" //使用我自己模拟封装的C++线程对象
#include <string>
#include <unistd.h> //休眠时间sleep
#include <ctime>    //产生随机数
#include "Task.hpp"

using namespace ThreadModule; // 打开自己封装的线程对象命名空间，std不打开，这样默认使用自己的
int a = 10;
using blockqueue_t = BlockQueue<Task>; // 这样的好处，就是阻塞对象类型，一改全改

void WaitAllThread(std::vector<Thread<blockqueue_t>> &threads)
{
    for (auto &thread : threads)
    {
        thread.Join(); // 回收改线程
    }
}

void Consumer(blockqueue_t &bq)
{
    while (true)
    {
        // 1.从blockqueue中取下一个任务
        Task t;
        bq.Pop(&t);
        // 2. 处理这个任务
        t.Excute();
        std::cout << "Consumer consum data is : " << t.ResultToString() << std::endl;
    }
}

void Productor(blockqueue_t &bq)
{
    srand((unsigned)time(nullptr) ^ pthread_self()); // 设计随机数种子，用于产生数据
    int cnt = 10;
    while (true)
    {
        sleep(1); // 生产的慢一点
        // 1. 获取任务
        int a = rand() % 10 + 1;
        usleep(1234);
        int b = rand() % 20 + 1;
        Task t(a, b);
        // 发送任务到阻塞队列中
        bq.Enqueue(t);
        std::cout << "Productor product data is : " << t.DebugToString() << std::endl;
    }
}

// func就是模板实现，传Conumer还是传Productor都可以，实例化出对应的版本
void StartComm(std::vector<Thread<blockqueue_t>> *threads, int num, blockqueue_t &bq, func_t<blockqueue_t> func)
{
    for (int i = 0; i < num; i++)
    {
        std::string name = "thread-" + std::to_string(i + 1);
        threads->emplace_back(func, bq, name); // 直接构造线程对象，高效
        threads->back().Start();               // 并启动线程
    }
}

// void StartConsumer(std::vector<Thread<blockqueue_t>>{* threads, int num, blockqueue_t* bq) 错误写法，不能传指针，值拷贝
// 一旦往下传，下面引用至一个局部的指针变量，销毁，引用了非法空间会崩溃
// 以下正确写法，干脆传引用，创建出来的所有进程的数据和执行函数的参数都引用至同一个阻塞队列
void StartConsumer(std::vector<Thread<blockqueue_t>> *threads, int num, blockqueue_t &bq)
{
    StartComm(threads, num, bq, Consumer);
}
// StartConsumer和StartProductor有相似，只是构造线程对象时，传入要执行的函数不一样罢了，但函数都是void(T&)所以可以模板，调用同一个函数，传不同函数参数罢了
void StartProductor(std::vector<Thread<blockqueue_t>> *threads, int num, blockqueue_t &bq)
{
    StartComm(threads, num, bq, Productor);
}

int main()
{
    blockqueue_t *bq = new blockqueue_t(5);    // new一个阻塞队列，并指明大小
    std::vector<Thread<blockqueue_t>> threads; // 存放所有的线程对象
    // std::vector<Thread<ThreadData>> threads;

    // 启动一批生产者，和一批消费者
    StartConsumer(&threads, 3, *bq);
    StartProductor(&threads, 1, *bq);
    // 回收所有线程
    WaitAllThread(threads);
    return 0;
}